package cn.csc.module.search.service.spu;

import cn.csc.framework.common.util.json.JsonUtils;
import cn.csc.module.search.controller.admin.sku.vo.SkuRespVO;
import cn.csc.module.search.controller.admin.sku.vo.SkuStockRespVO;
import cn.csc.module.search.controller.admin.spu.vo.SpuPageReqVO;
import cn.csc.module.search.controller.admin.spu.vo.SpuPageRespVO;
import cn.csc.module.search.convert.SkuConvert;
import cn.csc.module.search.convert.SkuStockConvert;
import cn.csc.module.search.convert.SpuConvert;
import cn.csc.module.search.dal.dataobject.sku.SkuDO;
import cn.csc.module.search.dal.dataobject.skuStock.SkuStockDO;
import cn.csc.module.search.dal.dataobject.spu.SpuDO;
import cn.csc.module.search.dal.es.sku.SkuRepository;
import cn.csc.module.search.dal.es.skuStock.SkuStockRepository;
import cn.csc.module.search.dal.es.spu.SpuRepository;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.WildcardQueryBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author liuchuang
 * @version 1.0
 * @modify
 * @since 2025/3/17 16:16
 */
@Slf4j
@Service
public class SpuService {

    @Autowired
    private SpuRepository spuRepository;

    @Autowired
    private SkuRepository skuRepository;

    @Autowired
    private SkuStockRepository skuStockRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchTemplate;


    public Page<SpuDO> searchSpuByName(String name, int page, int size) {
        return spuRepository.findByNameContaining(name, PageRequest.of(page, size));
    }

    /**
     * 根据ID查询
     */
    public Optional<SpuDO> findById(BigInteger id) {
        return spuRepository.findById(id);
    }


    /**
     * 分页查询，附带查询spu下的sku和库存
     *
     * @param reqVO 查询参数
     * @return
     */
    public Page<SpuPageRespVO> searchWithSkuAndStockOfPage(SpuPageReqVO reqVO) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 构建动态查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        Optional.ofNullable(reqVO.getName()).ifPresent(n -> boolQuery.filter(QueryBuilders.termQuery("name", n)));
        Optional.ofNullable(reqVO.getCategoryId()).ifPresent(c -> boolQuery.filter(QueryBuilders.termQuery("category_id", c)));
        if (!ObjectUtils.isEmpty(reqVO.getOnlineTime()) && reqVO.getOnlineTime().length == 2) {
            boolQuery.filter(QueryBuilders.rangeQuery("online_time").from(reqVO.getOnlineTime()[0]).to(reqVO.getOnlineTime()[1]));
        }
        //模糊查询
       Optional.ofNullable(reqVO.getName()).ifPresent(s->{
           WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("name.keyword", "*" +s + "*");
           boolQuery.must(wildcardQueryBuilder);
       });
        Optional.ofNullable(reqVO.getVerifyStatus()).ifPresent(s -> boolQuery.filter(QueryBuilders.termQuery("status", s)));
        //使用in查询
        Optional.ofNullable(reqVO.getVerifyStatus()).ifPresent(s -> {
            if (s == 0) {
                boolQuery.should(QueryBuilders.termQuery("verify_status", s));
                boolQuery.should(QueryBuilders.termQuery("verify_status", 15));
            } else {
                boolQuery.should(QueryBuilders.termQuery("verify_status", s));
            }
        });
        // and
        queryBuilder.withQuery(boolQuery);
        // 设置分页参数
        queryBuilder.withPageable(PageRequest.of(reqVO.getPageNo(), reqVO.getPageSize()));
        // 排序
        queryBuilder.withSorts(SortBuilders.fieldSort("create_time").order(SortOrder.DESC));

        // 执行查询
        SearchHits<SpuDO> searchHits = elasticsearchTemplate.search(queryBuilder.build(), SpuDO.class, IndexCoordinates.of("spu_index"));
        log.info("searchHits : {}", JsonUtils.toJsonString(searchHits));
        long totalHits = searchHits.getTotalHits();
        List<SpuDO> collect = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
        // 查询SPU下的SKU和stock
        List<SpuPageRespVO> respList = collect.stream().map(spu -> {
            SpuPageRespVO convert = SpuConvert.INSTANCE.convert(spu);
            List<SkuDO> bySpuId = skuRepository.findBySpuId(spu.getId());
            List<SkuRespVO> skuRespVOS = SkuConvert.INSTANCE.convertList1(bySpuId);
            List<BigInteger> skuIds = bySpuId.stream().map(SkuDO::getId).collect(Collectors.toList());
            List<SkuStockDO> bySkuIdIn = skuStockRepository.findBySkuIdIn(skuIds);
            List<SkuStockRespVO> skuStockRespVOS = SkuStockConvert.INSTANCE.convertList1(bySkuIdIn);
            convert.setSkus(merge(skuRespVOS, skuStockRespVOS));
            return convert;
        }).collect(Collectors.toList());

        return new PageImpl<>(respList, PageRequest.of(reqVO.getPageNo(), reqVO.getPageSize()), totalHits);
    }


    private List<SkuRespVO> merge(List<SkuRespVO> skus, List<SkuStockRespVO> skuStocks) {
        // 构建库存信息哈希映射
        Map<BigInteger, SkuStockRespVO> stockMap = skuStocks.stream()
                .collect(Collectors.toMap(
                        SkuStockRespVO::getSkuId,
                        skuStock -> skuStock
                ));
        return skus.stream()
                .peek(sku -> sku.setSkuStock(stockMap.get(sku.getId())))
                .collect(Collectors.toList());
    }


}
